// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "google/protobuf-expectation/proto-kit-converter.h"

#include <stddef.h>
#include <string>
#include <vector>

#include "google/protobuf-expectation/stubs/casts.h"
#include "glog/logging.h"
#include "google/protobuf-expectation/expectation.pb.h"
#include "google/protobuf-expectation/proto-kit.pb.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/message.h"

using std::string;
using std::vector;

using google::protobuf::Descriptor;
using google::protobuf::FieldDescriptor;
using google::protobuf::Message;
using google::protobuf::Reflection;
using google::protobuf_expectation::FieldExpectation;
using google::protobuf_expectation::MessageExpectation;
using google::protobuf_expectation::RepeatedMessageExpectation;
using google::protobuf_expectation::RepeatedValueExpectation;
using google::protobuf_expectation::ValueExpectation;
using google::protobuf_expectation::ValueKit;

namespace {

// Return true if the field is C++ message type.
inline bool IsCppTypeMessage(const FieldDescriptor* field) {
  return field != NULL &&
      field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE;
}

// Return true if the field is singular ValueKit type.
inline bool IsSingularValueKit(const FieldDescriptor* field) {
  return field != NULL &&  !field->is_repeated() && IsCppTypeMessage(field) &&
      field->message_type() == ValueKit::descriptor();
}

// Tests if the expectation is syntactically valid or not. For example,
// it returns false for "eq: 1 ne: 1" but does not for "eq: 1 and: < ne: 1 >".
bool IsValid(const ValueKit& expectation) {
  int num_this_expectation = 0;
  num_this_expectation += (expectation.has_eq() ? 1 : 0);
  num_this_expectation += (expectation.has_ne() ? 1 : 0);
  num_this_expectation += (expectation.has_le() ? 1 : 0);
  num_this_expectation += (expectation.has_lt() ? 1 : 0);
  num_this_expectation += (expectation.has_ge() ? 1 : 0);
  num_this_expectation += (expectation.has_gt() ? 1 : 0);
  num_this_expectation += (expectation.has_length() ? 1 : 0);
  num_this_expectation += (expectation.has_full_match_re() ? 1 : 0);
  num_this_expectation += (expectation.has_partial_match_re() ? 1 : 0);
  if (num_this_expectation > 1) {
    return false;
  }
  int num_is_expectation = 0;
  num_is_expectation += (expectation.has_is() ? 1 : 0);
  num_is_expectation += (expectation.has_is_not() ? 1 : 0);
  if (num_is_expectation > 1) {
    return false;
  }
  if (num_this_expectation == 0 && num_is_expectation == 0) {
    return false;
  }
  if (num_this_expectation > 0 && num_is_expectation > 0) {
    return false;
  }
  int num_that_expectation = 0;
  num_that_expectation += (expectation.and__size() > 0 ? 1 : 0);
  num_that_expectation += (expectation.or__size() > 0 ? 1 : 0);
  if (num_that_expectation > 1) {
    return false;
  }
  return true;
}

}  // namespace

namespace google {
namespace protobuf_expectation {

bool ConvertToGenericExpectation(
    const ValueKit& expectation,
    ValueExpectation* value_expectation) {
  if (!IsValid(expectation)) {
    return false;
  }

  value_expectation->set_debug_string(expectation.Utf8DebugString());
  if (expectation.has_is()) {
    value_expectation->set_operator_(ValueExpectation::IS);
    return ConvertToGenericExpectation(
        expectation.is(), value_expectation->add_value_expectation());
  }
  if (expectation.has_is_not()) {
    value_expectation->set_operator_(ValueExpectation::IS_NOT);
    return ConvertToGenericExpectation(
        expectation.is_not(), value_expectation->add_value_expectation());
  }

  ValueExpectation* this_value_expectation = value_expectation;
  if (expectation.and__size() > 0 || expectation.or__size() > 0) {
    if (expectation.and__size() > 0) {
      value_expectation->set_operator_(ValueExpectation::AND);
    } else {
      value_expectation->set_operator_(ValueExpectation::OR);
    }
    this_value_expectation = value_expectation->add_value_expectation();
  }

  if (expectation.has_eq()) {
    this_value_expectation->set_operator_(ValueExpectation::EQ);
    this_value_expectation->set_value(expectation.eq());
  } else if (expectation.has_ne()) {
    this_value_expectation->set_operator_(ValueExpectation::NE);
    this_value_expectation->set_value(expectation.ne());
  } else if (expectation.has_le()) {
    this_value_expectation->set_operator_(ValueExpectation::LE);
    this_value_expectation->set_value(expectation.le());
  } else if (expectation.has_lt()) {
    this_value_expectation->set_operator_(ValueExpectation::LT);
    this_value_expectation->set_value(expectation.lt());
  } else if (expectation.has_ge()) {
    this_value_expectation->set_operator_(ValueExpectation::GE);
    this_value_expectation->set_value(expectation.ge());
  } else if (expectation.has_gt()) {
    this_value_expectation->set_operator_(ValueExpectation::GT);
    this_value_expectation->set_value(expectation.gt());
  } else if (expectation.has_length()) {
    this_value_expectation->set_operator_(ValueExpectation::STRING_LENGTH);
    if (!ConvertToGenericExpectation(
            expectation.length(),
            this_value_expectation->add_value_expectation())) {
      return false;
    }
  } else if (expectation.has_full_match_re()) {
    this_value_expectation->set_operator_(ValueExpectation::FULL_MATCH_RE);
    this_value_expectation->set_value(expectation.full_match_re());
  } else if (expectation.has_partial_match_re()) {
    this_value_expectation->set_operator_(ValueExpectation::PARTIAL_MATCH_RE);
    this_value_expectation->set_value(expectation.partial_match_re());
  }

  for (int i = 0; i < expectation.and__size(); ++i) {
    if (!ConvertToGenericExpectation(
            expectation.and_(i), value_expectation->add_value_expectation())) {
      return false;
    }
  }
  for (int i = 0; i < expectation.or__size(); ++i) {
    if (!ConvertToGenericExpectation(
            expectation.or_(i), value_expectation->add_value_expectation())) {
      return false;
    }
  }
  return true;
}

namespace {
bool ConvertToRepeatedValueExpectation(
    const Descriptor* actual_descriptor,
    const Message& expectation,
    FieldExpectation* field_expectation) {
  CHECK_NOTNULL(field_expectation);
  field_expectation->set_debug_string(expectation.Utf8DebugString());
  const Reflection* reflection = expectation.GetReflection();
  vector<const FieldDescriptor*> expected_fields;
  reflection->ListFields(expectation, &expected_fields);
  for (int i = 0; i < expected_fields.size(); ++i) {
    const FieldDescriptor* expected_field = expected_fields[i];
    if (expected_field->name() == "field_size") {
      if (IsSingularValueKit(expected_field) &&
          reflection->HasField(expectation, expected_field)) {
        if (!ConvertToGenericExpectation(
                down_cast<const ValueKit&>(
                    reflection->GetMessage(expectation, expected_field)),
                field_expectation->mutable_field_size())) {
          return false;
        }
      } else {
        return false;
      }
    } else if (expected_field->name() == "for_each") {
      if (expected_field->is_repeated() || !IsCppTypeMessage(expected_field)) {
        return false;
      }
      const Message& for_each =
          reflection->GetMessage(expectation, expected_field);
      const FieldDescriptor* value_field =
          for_each.GetDescriptor()->FindFieldByName("value");
      if (IsSingularValueKit(value_field) &&
          for_each.GetReflection()->HasField(for_each, value_field)) {
        RepeatedValueExpectation::ForEach* dst = field_expectation->
            mutable_repeated_value_expectation()->mutable_for_each();
        if (!ConvertToGenericExpectation(
                down_cast<const ValueKit&>(
                    for_each.GetReflection()->GetMessage(for_each,
                                                         value_field)),
                dst->mutable_value_expectation())) {
          return false;
        }
      } else {
        return false;
      }
    } else if (expected_field->name() == "have") {
      if (!expected_field->is_repeated() || !IsCppTypeMessage(expected_field)) {
        return false;
      }
      const int field_size = reflection->FieldSize(expectation, expected_field);
      for (int j = 0; j < field_size; ++j) {
        const Message& have =
            reflection->GetRepeatedMessage(expectation, expected_field, j);
        const FieldDescriptor* value_field =
            have.GetDescriptor()->FindFieldByName("value");
        RepeatedValueExpectation::Have* dst = NULL;
        if (IsSingularValueKit(value_field) &&
            have.GetReflection()->HasField(have, value_field)) {
          dst = field_expectation->mutable_repeated_value_expectation()->
              add_have();
          if (!ConvertToGenericExpectation(
                  down_cast<const ValueKit&>(
                      have.GetReflection()->GetMessage(have, value_field)),
                  dst->mutable_value_expectation())) {
            return false;
          }
        } else {
          return false;
        }
        const FieldDescriptor* at_index_field =
            have.GetDescriptor()->FindFieldByName("at_index");
        if (IsSingularValueKit(at_index_field) &&
            have.GetReflection()->HasField(have, at_index_field)) {
          if (!ConvertToGenericExpectation(
                  down_cast<const ValueKit&>(
                      have.GetReflection()->GetMessage(have, at_index_field)),
                  dst->mutable_at_index())) {
            return false;
          }
        }
      }
    } else if (expected_field->name() == "do_not_have") {
      if (!expected_field->is_repeated() || !IsCppTypeMessage(expected_field)) {
        return false;
      }
      const int field_size = reflection->FieldSize(expectation, expected_field);
      for (int j = 0; j < field_size; ++j) {
        const Message& do_not_have =
            reflection->GetRepeatedMessage(expectation, expected_field, j);
        const FieldDescriptor* value_field =
            do_not_have.GetDescriptor()->FindFieldByName("value");
        if (IsSingularValueKit(value_field) &&
            do_not_have.GetReflection()->HasField(do_not_have, value_field)) {
          RepeatedValueExpectation::DoNotHave* dst = field_expectation->
              mutable_repeated_value_expectation()->add_do_not_have();
          if (!ConvertToGenericExpectation(
                  down_cast<const ValueKit&>(
                      do_not_have.GetReflection()->
                      GetMessage(do_not_have, value_field)),
                  dst->mutable_value_expectation())) {
            return false;
          }
        } else {
          return false;
        }
      }
    } else {
      // Unknown field name.
      return false;
    }
  }
  return true;
}

bool ConvertToRepeatedMessageExpectation(
    const Descriptor* actual_descriptor,
    const Message& expectation,
    FieldExpectation* field_expectation) {
  CHECK_NOTNULL(field_expectation);
  field_expectation->set_debug_string(expectation.Utf8DebugString());
  const Reflection* reflection = expectation.GetReflection();
  vector<const FieldDescriptor*> expected_fields;
  reflection->ListFields(expectation, &expected_fields);
  for (int i = 0; i < expected_fields.size(); ++i) {
    const FieldDescriptor* expected_field = expected_fields[i];
    if (expected_field->name() == "field_size") {
      if (IsSingularValueKit(expected_field)) {
        if (!ConvertToGenericExpectation(
                down_cast<const ValueKit&>(
                    reflection->GetMessage(expectation, expected_field)),
                field_expectation->mutable_field_size())) {
          return false;
        }
      } else {
        return false;
      }
    } else if (expected_field->name() == "for_each") {
      if (expected_field->is_repeated() || !IsCppTypeMessage(expected_field)) {
        return false;
      }
      if (!ConvertToGenericExpectation(
              actual_descriptor,
              reflection->GetMessage(expectation, expected_field),
              field_expectation->mutable_repeated_message_expectation()->
              mutable_for_each()->mutable_message_expectation())) {
        return false;
      }
    } else if (expected_field->name() == "have") {
      if (!expected_field->is_repeated() || !IsCppTypeMessage(expected_field)) {
        return false;
      }
      const int field_size = reflection->FieldSize(expectation, expected_field);
      for (int j = 0; j < field_size; ++j) {
        const Message& have =
            reflection->GetRepeatedMessage(expectation, expected_field, j);
        RepeatedMessageExpectation::Have* dst =
            field_expectation->mutable_repeated_message_expectation()->
            add_have();
        if (!ConvertToGenericExpectation(
                actual_descriptor, have, dst->mutable_message_expectation())) {
          return false;
        }
        const FieldDescriptor* at_index_field =
            have.GetDescriptor()->FindFieldByName("at_index");
        if (IsSingularValueKit(at_index_field) &&
            have.GetReflection()->HasField(have, at_index_field)) {
          if (!ConvertToGenericExpectation(
                  down_cast<const ValueKit&>(
                      have.GetReflection()->GetMessage(have, at_index_field)),
                  dst->mutable_at_index())) {
            return false;
          }
        }
      }
    } else if (expected_field->name() == "do_not_have") {
      if (!expected_field->is_repeated() || !IsCppTypeMessage(expected_field)) {
        return false;
      }
      const int field_size = reflection->FieldSize(expectation, expected_field);
      for (int j = 0; j < field_size; ++j) {
        if (!ConvertToGenericExpectation(
                actual_descriptor,
                reflection->GetRepeatedMessage(expectation, expected_field, j),
                field_expectation->mutable_repeated_message_expectation()->
                add_do_not_have()->mutable_message_expectation())) {
          return false;
        }
      }
    } else {
      // Unknown field name.
      return false;
    }
  }
  return true;
}
}  // namespace

bool ConvertToGenericExpectation(const Descriptor* actual_descriptor,
                                 const Message& expectation,
                                 MessageExpectation* message_expectation) {
  CHECK_NOTNULL(actual_descriptor);
  CHECK_NOTNULL(message_expectation);
  message_expectation->Clear();
  message_expectation->set_debug_string(expectation.Utf8DebugString());
  const Reflection* reflection = expectation.GetReflection();
  vector<const FieldDescriptor*> expected_fields;
  reflection->ListFields(expectation, &expected_fields);
  for (int i = 0; i < expected_fields.size(); ++i) {
    const FieldDescriptor* expected_field = expected_fields[i];
    const FieldDescriptor* actual_field =
        actual_descriptor->FindFieldByName(expected_field->name());
    if (actual_field == NULL) {
      if (expected_field->name() == "at_index") {
        // It's ok that at_index field is missing in the actual proto.
        continue;
      }
      // TODO(user): consider extensions.
      return false;
    }
    if (expected_field->is_repeated()) {
      LOG(ERROR) << "Fields in an expectation proto should not be repeated: "
                 << expected_field->DebugString();
      return false;
    }
    if (!IsCppTypeMessage(expected_field)) {
      LOG(ERROR) << "Fields in an expectation proto should be message type: "
                 << expected_field->DebugString();
      return false;
    }
    if (IsSingularValueKit(expected_field)) {
      // A value expectation.
      if (actual_field->is_repeated() || IsCppTypeMessage(actual_field)) {
        LOG(ERROR) << "Type mismatch between the actual proto and "
                   << "the expectation proto.";
        LOG(ERROR) << "  actual: " << actual_field->DebugString();
        LOG(ERROR) << "  expectation: " << expected_field->DebugString();
        return false;
      }
      FieldExpectation* field_expectation =
          message_expectation->add_field_expectation();
      field_expectation->set_field_name(expected_field->name());
      if (!ConvertToGenericExpectation(
              down_cast<const ValueKit&>(
                  reflection->GetMessage(expectation, expected_field)),
              field_expectation->mutable_value_expectation())) {
        return false;
      }
    } else {
      // A repeated value expectation, a message expectation or
      // a repeated message expectation.
      if (!IsCppTypeMessage(actual_field)) {
        // A repeated value expectation.
        if (!actual_field->is_repeated()) {
          LOG(ERROR) << "Type mismatch between the actual proto and "
                     << "the expectation proto.";
          LOG(ERROR) << "  actual: " << actual_field->DebugString();
          LOG(ERROR) << "  expectation: " << expected_field->DebugString();
          return false;
        }
        FieldExpectation* field_expectation =
            message_expectation->add_field_expectation();
        field_expectation->set_field_name(expected_field->name());
        if (!ConvertToRepeatedValueExpectation(
                actual_field->message_type(),
                reflection->GetMessage(expectation, expected_field),
                field_expectation)) {
          return false;
        }
      } else {
        // A message expectation or a repeated message expectation.
        FieldExpectation* field_expectation =
            message_expectation->add_field_expectation();
        field_expectation->set_field_name(expected_field->name());
        if (!actual_field->is_repeated()) {
          // A message expectation
          if (!ConvertToGenericExpectation(
                  actual_field->message_type(),
                  reflection->GetMessage(expectation, expected_field),
                  field_expectation->mutable_message_expectation())) {
            return false;
          }
        } else {
          // A repeated message expectation.
          if (!ConvertToRepeatedMessageExpectation(
                  actual_field->message_type(),
                  reflection->GetMessage(expectation, expected_field),
                  field_expectation)) {
            return false;
          }
        }
      }
    }
  }
  return true;
}

}  // namespace protobuf_expectation
}  // namespace google
